package com.hyt.it.ogt.kq.service.gov.service.impl;

import static com.hyt.it.ogt.kq.common.gov.enums.CacheKeyEnum.CREATE_MOCK_ROOM_DATA_KEY;
import static com.hyt.it.ogt.kq.common.gov.enums.CacheKeyEnum.CREATE_MOCK_ROOM_DATA_PROGRESS_KEY;
import static com.hyt.it.ogt.kq.common.gov.enums.CacheKeyEnum.CREATE_MOCK_ROOM_DATA_PROGRESS_TOTAL_KEY;
import static com.hyt.it.ogt.kq.common.gov.enums.CacheKeyEnum.CREATE_ROOM_DATA_KEY;
import static com.hyt.it.ogt.kq.common.gov.enums.CacheKeyEnum.CREATE_ROOM_DATA_PROGRESS_KEY;
import static com.hyt.it.ogt.kq.common.gov.enums.CacheKeyEnum.CREATE_ROOM_DATA_PROGRESS_TOTAL_KEY;

import java.io.File;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.hyt.it.ogt.kq.service.gov.model.dto.ProgressResult;
import com.hyt.progress.service.ProgressManager;
import org.apache.commons.io.FileUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.kq.common.gov.constant.examsys.TypeEnum;
import com.hyt.it.ogt.kq.common.gov.constant.examsys.gce.GCEExamTypeEnum;
import com.hyt.it.ogt.kq.service.gov.model.entity.GceRoomData;
import com.hyt.it.ogt.kq.service.gov.model.entity.ObtRoomData;
import com.hyt.it.ogt.kq.service.gov.model.entity.Task;
import com.hyt.it.ogt.kq.service.gov.model.entity.TaskConfig;
import com.hyt.it.ogt.kq.service.gov.model.param.DataLockUpdateParam;
import com.hyt.it.ogt.kq.service.gov.roomdata.RoomDataService;
import com.hyt.it.ogt.kq.service.gov.service.IAsyncService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskConfigService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeRoomCandidateService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeRoomService;
import com.hyt.it.ogt.kq.service.gov.service.TimeRoomExaminerCacheService;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
public class AsyncServiceImpl implements IAsyncService {

    @Resource
    private ITimeRoomCandidateService iTimeRoomCandidateService;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private RoomDataService roomDataService;

    @Resource
    private ITaskConfigService iTaskConfigService;

    @Resource
    private ITimeRoomService iTimeRoomService;

    @Resource
    private ITaskService iTaskService;

    @Resource
    private TimeRoomExaminerCacheService timeRoomExaminerCacheService;


    @SneakyThrows
    @Override
    public void updateTaskDataLock(DataLockUpdateParam dataLockUpdateParam) {
        ProgressManager.updateProgress(3L);
        iTimeRoomCandidateService.updateTaskDataLock(dataLockUpdateParam);
        log.info("清理开始清理批次下所有的考务人员的关联关系缓存");
        timeRoomExaminerCacheService.clearUserProctorCache(null, dataLockUpdateParam.getTaskId());
        ProgressManager.finish(ProgressResult.builder().success(Boolean.TRUE).build());
    }

    private Map<String, String> getMap() {
        Map<String,String> dataMap = new HashMap<String,String>(16);
        dataMap.put("admissionNumber", "zkz");
        dataMap.put("idNumber", "123");
        dataMap.put("name", "lily");
        dataMap.put("placeAddress", "shenzhen");
        dataMap.put("roomAddress", UUIDUtils.newSortUUID());
        dataMap.put("sex", "男");
        dataMap.put("taskName", "2021语文考试");
        dataMap.put("subject", "语文");
        dataMap.put("avatar", "https://hyt-yks-1259052082.cos.ap-guangzhou.myqcloud.com/bmPhoto/b7478a624fae4e589d934914c3f4291f/temp/c10ca310-e05a-4f2e-b22f-92b8c36c5850.jpg");
        //dataMap.put("avatar", "C:\\Users\\lhh\\Desktop\\1.jpg");
        dataMap.put("roomName_1", "考场1");
        dataMap.put("seat_1", "1");
        dataMap.put("subject_1", "科目1");
        dataMap.put("date_1", "2021-1-1");
        return dataMap;
    }

    @Override
    @Async("executor")
    public void createRoomData(String taskId, String officeId) {
        try {
            //区分不同类型考试系统的打包方式
            Optional<String> optional = iTaskConfigService.getExamSystemTypeByTaskId(taskId);
            optional.ifPresent(type -> {
                //获取进度条完成数量key
                String progressKey = CREATE_ROOM_DATA_PROGRESS_KEY.getCacheEnumKey(taskId);
                //生成考务包
                switch (TypeEnum.getType(type)) {
                    //生成OBT考试系统考务包
                    case OBT:
                        this.createObtRoomData(taskId, officeId, progressKey, CREATE_ROOM_DATA_PROGRESS_KEY.getExpireSeconds());
                        break;
                    //生成GCE考试系统考务包
                    case GCE:
                        this.createGceRoomData(taskId, officeId, progressKey, CREATE_ROOM_DATA_PROGRESS_KEY.getExpireSeconds(),
                                GCEExamTypeEnum.FORMAL.getType());
                        break;
                }
            });
        } finally {
            //清除分布式锁
            String creatingKey = CREATE_ROOM_DATA_KEY.getCacheEnumKey(taskId);
            Boolean success = redisTemplate.delete(creatingKey);
            success = Optional.ofNullable(success).orElse(false);
            if (!success) {
                log.error("清除生成考务包分布式锁失败，key值: {}", creatingKey);
            }
        }
    }

    @Override
    @Async("executor")
    public void createMockRoomData(String taskId, String officeId) {
        try {
            //获取进度条完成数量key
            String progressKey = CREATE_MOCK_ROOM_DATA_PROGRESS_KEY.getCacheEnumKey(taskId);
            //生成模拟测试考务包
            //目前暂时只支持生成GCE考试系统模拟测试考务包
            this.createGceRoomData(taskId, officeId, progressKey, CREATE_MOCK_ROOM_DATA_PROGRESS_KEY.getExpireSeconds(),
                    GCEExamTypeEnum.MOCK.getType());
        } finally {
            //清除分布式锁
            String creatingKey = CREATE_MOCK_ROOM_DATA_KEY.getCacheEnumKey(taskId);
            Boolean success = redisTemplate.delete(creatingKey);
            success = Optional.ofNullable(success).orElse(false);
            if (!success) {
                log.error("清除生成模拟测试考务包分布式锁失败，key值: {}", creatingKey);
            }
        }
    }

    /**
     * 生成OBT考试系统考务包
     * @param taskId 考试任务id
     * @param officeId 机构id
     * @param progressKey 进度条key
     * @param progressKeyExpireSeconds 进度条过期时间
     */
    private void createObtRoomData(String taskId, String officeId, String progressKey, long progressKeyExpireSeconds) {
        //查询场次信息
        List<ObtRoomData> rooDataList = iTimeRoomService.getObtRoomDataList(taskId);
        //获取进度条总量key
        String progressTotalKey = CREATE_ROOM_DATA_PROGRESS_TOTAL_KEY.getCacheEnumKey(taskId);
        //设置进度条总量
        redisTemplate.opsForValue().set(progressTotalKey, rooDataList.size(), CREATE_ROOM_DATA_PROGRESS_TOTAL_KEY.getExpireSeconds(), TimeUnit.SECONDS);
        //按场次生成考务包(异步)
        CompletableFuture.allOf(rooDataList.stream()
                .map(roomData -> roomDataService.createObtRoomDataByTimeRoom(taskId, roomData, officeId, progressKey, progressKeyExpireSeconds))
                .toArray(CompletableFuture[]::new)).join();
    }

    /**
     * 生成GCE考试系统考务包
     * @param taskId 考试任务id
     * @param officeId 机构id
     * @param progressKey 进度条key
     * @param progressKeyExpireSeconds 进度条key过期时间
     * @param type 考务包类型
     */
    @SneakyThrows
    private void createGceRoomData(String taskId, String officeId, String progressKey, long progressKeyExpireSeconds, String type) {
        //创建考务包生成临时目录
        final File tempDirectory = Files.createTempDirectory("GceRoomData_" + type
                + "_" + UUIDUtils.newSortUUID()).toFile();
        try {
            //查询场次信息
            List<GceRoomData> gceRooDataList = iTimeRoomService.getGceRoomDataList(taskId);
            //获取生成考务包进度条总量key
            String progressTotalKey = this.getGceProgressTotalKey(taskId, type);
            //设置进度条总量
            redisTemplate.opsForValue().set(progressTotalKey, gceRooDataList.size(), CREATE_MOCK_ROOM_DATA_PROGRESS_TOTAL_KEY.getExpireSeconds(), TimeUnit.SECONDS);
            //获取考试任务信息
            Task task = iTaskService.getById(taskId);
            //查询考试配置
            Map<String, String> configMap = iTaskConfigService.getByTaskId(taskId).stream()
                    .collect(Collectors.toMap(TaskConfig::getCode, TaskConfig::getValue));
            //按场次生成考务包(createGceRoomDataByTimeRoom为异步方法，先执行完的线程会被阻塞，直到gceRooDataList遍历完毕后才会执行接下来的代码)
            CompletableFuture.allOf(gceRooDataList.stream()
                    .map(gceRoomData -> roomDataService.createGceRoomDataByTimeRoom(task, gceRoomData, officeId,
                            progressKey, progressKeyExpireSeconds, tempDirectory, configMap, type))
                    .toArray(CompletableFuture[]::new)).join();
        } finally {
            //删除生成考务包临时目录
            FileUtils.deleteDirectory(tempDirectory);
        }
    }

    /**
     * 获取生成GCE考务包进度条总量key
     * @param taskId 考试任务id
     * @param type 考务包类型
     * @return 生成GCE考务包进度条总量key
     */
    private String getGceProgressTotalKey(String taskId, String type) {
        if (GCEExamTypeEnum.FORMAL.getType().equals(type)) {
            //获取生成正式考试考务包进度条总量key
            return CREATE_ROOM_DATA_PROGRESS_TOTAL_KEY.getCacheEnumKey(taskId);
        }
        //获取生成模拟测试考务包进度条总量key
        return CREATE_MOCK_ROOM_DATA_PROGRESS_TOTAL_KEY.getCacheEnumKey(taskId);
    }
}
